package org.helgoboss.janana_tray_resource_management

import org.osgi.framework.{BundleActivator, BundleContext, Bundle, BundleListener, BundleEvent, Version}
import org.helgoboss.janana_tray.{JananaTray, JananaTrayAddOn}
import org.helgoboss.osgi_additions.{OsgiLogging, OsgiBundleWatcher, OsgiConsumer, OsgiContext, OsgiProvider}
import org.osgi.service.obr.{RepositoryAdmin, Resource}
import org.osgi.service.packageadmin.PackageAdmin
import org.helgoboss.caching_obr.CachingRepositoryAdmin
import java.awt.MenuItem
import java.awt.event.{ActionEvent, ActionListener}
import java.net.URL


class JananaTrayResourceManagement extends OsgiContext with OsgiLogging with OsgiBundleWatcher with OsgiConsumer with OsgiProvider {

    def repositoryAdmin = requiredService[RepositoryAdmin]
    def packageAdmin = requiredService[PackageAdmin]

    whenBundleActive {
        trayAddOn.providesService[JananaTrayAddOn]
        watchBundles {
            case ModifiedBundle(_, _) => bundleResourceManagementView.update
            case AddingBundle(_, _) => bundleResourceManagementView.update
            case RemovedBundle(bundle, _) => bundleResourceManagementView.update
        }
    }

    
    private var resourceManagementItem: MenuItem = _
    
    val trayAddOn = new JananaTrayAddOn {
        lazy val menuItem = {
            val resourceManagementListener = new ActionListener {
                def actionPerformed(e: ActionEvent) {
                    bundleResourceManagementView.show
                }
            }
                    
            val tmp = new MenuItem("Resource Management")
            tmp.addActionListener(resourceManagementListener)
            tmp
        }
    
        def addToJananaTray(tray: JananaTray) {
            tray.getIcon.getPopupMenu.insert(menuItem, 0)
        }

        def removeFromJananaTray(tray: JananaTray) {
            tray.getIcon.getPopupMenu.remove(menuItem)
            bundleResourceManagementView.dispose
        }
    }
    
    lazy val bundleResourceManagementView = new BundleResourceManagementView {
    
        def installBundleResource(bundleResource: BundleResource) {
            val resource = bundleResource.resource getOrElse error("Bundle doesn't have a corresponding resource")
            val resolver = repositoryAdmin.resolver
            resolver.add(resource)
            if (resolver.resolve) {
                resolver.deploy(true)
            } else {
                val unsatisfiedString = resolver.getUnsatisfiedRequirements.map(_.getFilter).mkString(", ")
                log.debug("Could not resolve [" + resource.getSymbolicName + "]. Unsatisfied requirements: [" + unsatisfiedString + "]")
            }
        }
            
        def findBundleResources(wildcard: String): Seq[BundleResource] = {
            /* Put installed bundles in fast to query hash map */
            
            case class SimpleBundleKey(val symbolicName: String, val version: Version)
            
            val installedBundles = new collection.mutable.HashMap[SimpleBundleKey, Bundle]
            
            for (bundle <- bundleContext.getBundles) {
                if (bundle.getSymbolicName.contains(wildcard)) {
                    val key = SimpleBundleKey(bundle.getSymbolicName, bundle.getVersion)
                    installedBundles += key -> bundle
                }
            }
            
            
            /* Find repository resources */
            
            val resources: Iterable[Resource] = optionalService[RepositoryAdmin] match {
                case Some(admin) => 
                    val filter = "(symbolicname=*" + wildcard + "*)"
                    admin.discoverResources(filter)
                case None => Nil
            }
            
            
            /* Map resources to installed bundles */
            
            val bundleResources = new collection.mutable.ListBuffer[BundleResource]
            
            for (resource <- resources) {
                val bundleResource = new BundleResource
                bundleResource.resource = Some(resource)
                val key = SimpleBundleKey(resource.getSymbolicName, resource.getVersion)
                installedBundles.get(key) match {
                    case Some(bundle) =>
                        bundleResource.bundle = Some(bundle)
                        installedBundles -= key
                        
                    case None =>
                }
                bundleResources += bundleResource
            }
            
            
            /* Add bundles without corresponding resource */
            
            for (bundle <- installedBundles.values) {
                val bundleResource = new BundleResource
                bundleResource.bundle = Some(bundle)
                bundleResources += bundleResource
            }
            
            bundleResources
        }
        
        
        
        def uninstallBundleResource(bundleResource: BundleResource) {
            val bundle = bundleResource.bundle getOrElse error("Resource doesn't have a corresponding bundle")
            if (bundle.getState != Bundle.UNINSTALLED) {
                bundle.uninstall
            }
        }
        
        def refreshAllBundles {
            log.debug("Refresh bundles")
            packageAdmin.refreshPackages(null)
        }
        
        def refreshBundleResource(bundleResource: BundleResource) {
            uninstallBundleResource(bundleResource)
            installBundleResource(bundleResource)
        }
        
        def refreshRepositories {
            val repositories = repositoryAdmin.listRepositories
            repositories.foreach(repositoryAdmin removeRepository _.getURL)
            
            repositories.foreach { repo =>
                notifyRefreshingRepository(repo)
                optionalService[CachingRepositoryAdmin] match {
                    case Some(cachingRepositoryAdmin) => 
                        /* Caching repository admin is available. This one has a method that can report the download progress. */
                        cachingRepositoryAdmin.addRepository(repo.getURL, Some(updateRepositoryRefreshingProgress))
                    case None =>
                        /* Use the normal repository admin which is not capable of reporting the download progress. */
                        repositoryAdmin.addRepository(repo.getURL)
                }
            }
        }
    }
    
    
        
}



